home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
Libraries
/
Sherlock 2.0
/
DevLibSrc
/
Main_DevLib
/
LIBmem.c
< prev
next >
Wrap
Text File
|
1996-03-16
|
16KB
|
573 lines
/*
devlib: Memory management routines and node allocation routines.
source: LIBmem.c
started: September 20, 1985
version: January 7, 1994.
November 15, 1995.
Added mem_free_life_node.
November 7, 1995.
Replace sprintf with cvt_l2s.
Call end_abort instead of abort.
July 13, 1994.
Bug fix to mem_new_block (!)
*/
#include <LIBlib.h>
#include <LIBend.h>
#include <LIBlist.h>
#include <LIBmem.h>
#include <LIBobj.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
Define global variables.
*/
mem_life * mem_life_list = NULL;
/*
Define local variables.
*/
static long mem_max_node_tag = 12; /* strlen("big blocks:")+1 */
static long mem_max_byte_tag = 11; /* strlen("big bytes:")+1 */
/*
Compute the maximum statistics for the given lifetime.
*/
void
mem_compute_max_stats (register mem_life * life)
{
FTAG("mem_compute_max_stats");
register mem_stat * stat_p = NULL;
STATB(ftag);
/* Update the global statistics. */
life -> mem_max_blocks = max(life -> mem_max_blocks, life -> mem_cur_blocks);
life -> mem_max_nodes = max(life -> mem_max_nodes, life -> mem_cur_nodes);
life -> mem_max_bytes = max(life -> mem_max_bytes, life -> mem_cur_bytes);
life -> mem_max_waste = max(life -> mem_max_waste, life -> mem_cur_waste);
life -> mem_max_big_blocks = max(life -> mem_max_big_blocks, life -> mem_cur_big_blocks);
life -> mem_max_big_bytes = max(life -> mem_max_big_bytes, life -> mem_cur_big_bytes);
/* Update the maxima statistics in all attached statistics nodes. */
for (stat_p = life -> mem_life_stat_list; stat_p; stat_p = stat_p -> next) {
stat_p -> mem_max_stat_nodes = max(stat_p -> mem_max_stat_nodes, stat_p -> mem_cur_stat_nodes);
stat_p -> mem_max_stat_bytes = max(stat_p -> mem_max_stat_bytes, stat_p -> mem_cur_stat_bytes);
}
STATX(ftag);
}
/*
Print memory statistics.
Unlike object statistics, these are available in the production program.
*/
#define NODES_TAG "cur nodes"
#define BYTES_TAG "cur bytes"
#define MAX_NODES_TAG "max nodes"
#define MAX_BYTES_TAG "max bytes"
#define TOT_NODES_TAG "tot nodes"
#define TOT_BYTES_TAG "tot bytes"
#ifndef PRODUCTION /* Define this routine only if there is a Sherlock window. */
void
mem_dump_stats(register mem_life * life)
{
FTAG("mem_dump_stats");
register mem_stat * stat_p = NULL;
register mem_life * mlp = NULL;
long tag_width = mem_max_node_tag;
long bytes_width = strlen(NODES_TAG);
long nodes_width = strlen(BYTES_TAG);
long max_bytes_width = strlen(NODES_TAG);
long max_nodes_width = strlen(BYTES_TAG);
long tot_bytes_width = strlen(NODES_TAG);
long tot_nodes_width = strlen(BYTES_TAG);
long tot_max_bytes = 0;
long tot_max_nodes = 0;
long tot_cur_bytes = 0;
long tot_cur_nodes = 0;
long blocks_width = 1;
long waste_width = 1;
long avail_width = 1;
char buf [CVT_BUF_SIZE];
STATB(ftag);
/* Recalculate the maximum statistics. */
mem_compute_max_stats(life);
/*
Update the totals.
The current statistics will be cleared after we print them.
*/
life -> mem_tot_blocks += life -> mem_cur_blocks;
life -> mem_tot_nodes += life -> mem_cur_nodes;
life -> mem_tot_bytes +=life -> mem_cur_bytes;
life -> mem_tot_waste += life -> mem_cur_waste;
life -> mem_tot_big_blocks += life -> mem_cur_big_blocks;
life -> mem_tot_big_bytes += life -> mem_cur_big_bytes;
/* Compute the sizes of maximum column widths in *all* lifetimes. */
for (mlp = mem_life_list; mlp; mlp = mlp -> mem_life_list) {
for (stat_p = mlp -> mem_life_stat_list; stat_p; stat_p = stat_p -> next) {
tag_width = max(tag_width, strlen(stat_p -> mem_stat_node_tag));
}
#if 0 /* Avoid sprintf so we don't pull in the whole floating library. */
bytes_width = max(bytes_width, sprintf(buf, "%ld", mlp -> mem_tot_bytes));
nodes_width = max(nodes_width, sprintf(buf, "%ld", mlp -> mem_tot_nodes));
blocks_width = max(blocks_width, sprintf(buf, "%ld", mlp -> mem_tot_blocks));
waste_width = max(waste_width, sprintf(buf, "%ld", mlp -> mem_tot_waste));
avail_width = max(avail_width, sprintf(buf, "%ld", mlp -> mem_last_avail));
#else
bytes_width = max(bytes_width, cvt_l2s(buf, mlp -> mem_tot_bytes));
nodes_width = max(nodes_width, cvt_l2s(buf, mlp -> mem_tot_nodes));
blocks_width = max(blocks_width, cvt_l2s(buf, mlp -> mem_tot_blocks));
waste_width = max(waste_width, cvt_l2s(buf, mlp -> mem_tot_waste));
avail_width = max(avail_width, cvt_l2s(buf, mlp -> mem_last_avail));
#endif
}
max_bytes_width = max(max_bytes_width, bytes_width);
max_nodes_width = max(max_nodes_width, nodes_width);
tot_bytes_width = max(tot_bytes_width, bytes_width);
tot_nodes_width = max(tot_nodes_width, nodes_width);
/*
Print the headings for the statistics.
The 2's in these pad widths compensate for the calls to ecs() or es(": ").
These calls insure that each column starts with at least one blank.
*/
ecnl();
es("Totals for "); es(life -> mem_life_name); es("...\n");
epads(" ", tag_width);
epads(TOT_NODES_TAG, tot_nodes_width+2);
epads(TOT_BYTES_TAG, tot_bytes_width+2);
epads(MAX_NODES_TAG, max_nodes_width+2);
epads(MAX_BYTES_TAG, max_bytes_width+2);
epads(NODES_TAG, nodes_width+2);
epads(BYTES_TAG, bytes_width+2);
enl();
/* Print the statistics for all attached statistics nodes. */
for (stat_p = life -> mem_life_stat_list; stat_p; stat_p = stat_p -> next) {
/* Update the totals. */
stat_p -> mem_tot_stat_nodes += stat_p -> mem_cur_stat_nodes;
stat_p -> mem_tot_stat_bytes += stat_p -> mem_cur_stat_bytes;
/* print the statistics. */
{
/* Print the tag field. */
ecnl();
epads(stat_p -> mem_stat_node_tag, tag_width); es(": ");
/* Print the tot statistics. */
epadlong(stat_p -> mem_tot_stat_nodes, tot_nodes_width); ecs();
epadlong(stat_p -> mem_tot_stat_bytes, tot_bytes_width); ecs();
/* Print the max statistics. */
epadlong(stat_p -> mem_max_stat_nodes, max_nodes_width); ecs();
epadlong(stat_p -> mem_max_stat_bytes, max_bytes_width); ecs();
tot_max_nodes += stat_p -> mem_max_stat_nodes;
tot_max_bytes += stat_p -> mem_max_stat_bytes;
/* Print the current statistics. */
epadlong(stat_p -> mem_cur_stat_nodes, nodes_width); ecs();
epadlong(stat_p -> mem_cur_stat_bytes, bytes_width);
tot_cur_nodes += stat_p -> mem_cur_stat_nodes;
tot_cur_bytes += stat_p -> mem_cur_stat_bytes;
enl();
}
/* Reset the current stats so they won't be counted twice! */
stat_p -> mem_cur_stat_nodes = 0;
stat_p -> mem_cur_stat_bytes = 0;
}
/* Print the totals lines. */
{
/* Print the grand totals. */
ecnl();
epads("TOTALS", tag_width); es(": ");
epadlong(life -> mem_tot_nodes, tot_nodes_width); ecs();
epadlong(life -> mem_tot_bytes, tot_bytes_width); ecs();
epadlong(tot_max_nodes, max_nodes_width); ecs();
epadlong(tot_max_bytes, max_bytes_width); ecs();
epadlong(tot_cur_nodes, nodes_width); ecs();
epadlong(tot_cur_bytes, bytes_width); enl();
/* Print the maxima. */
ecnl();
epads("MAXIMA", tag_width); es(": ");
epadlong(life -> mem_max_nodes, nodes_width); ecs();
epadlong(life -> mem_max_bytes, bytes_width);
enl();
/* Print the block statistics. */
/* Always leave room for "N/A" */
avail_width = max(3, avail_width);
es("tot blocks: "); epadlong(life -> mem_tot_blocks, blocks_width); ecs();
es("tot waste: "); epadlong(life -> mem_tot_waste, waste_width); ecs();
es("tot avail: "); epadlong(life -> mem_last_avail, avail_width); ecs();
epads("big blocks: ", 2); elong(life -> mem_tot_big_blocks); ecs();
es("big bytes: "); elong(life -> mem_tot_big_bytes);
enl();
es("max blocks: "); epadlong(life -> mem_max_blocks, blocks_width); ecs();
es("max waste: "); epadlong(life -> mem_max_waste, waste_width); ecs();
es("max avail: "); epads("N/A", avail_width); ecs();
epads("big blocks: ", 2); elong(life -> mem_max_big_blocks); ecs();
es("big bytes: "); elong(life -> mem_max_big_bytes);
ecnl();
}
/* Reset the current stats so they won't be counted twice later. */
life -> mem_cur_blocks = 0;
life -> mem_cur_nodes = 0;
life -> mem_cur_bytes = 0;
life -> mem_cur_waste = 0;
life -> mem_cur_big_blocks = 0;
life -> mem_cur_big_bytes = 0;
STATX(ftag);
}
#endif /* n PRODUCTION */
/*
Free all blocks of the indicated lifetime and update statistics.
Do _not_ free the lifetime itself or remove the lifetime from the list of lifetimes.
*/
void
mem_free_life(register mem_life * life)
{
FTAG("mem_free_life");
register mem_stat * stat_p = NULL;
STATB(ftag);
ASSERT(life);
/* Update the maximum statistic in all attached statistics nodes. */
for (stat_p = life -> mem_life_stat_list; stat_p; stat_p = stat_p -> next) {
/* Update the maxima statistics.*/
stat_p -> mem_max_stat_nodes = max(stat_p -> mem_max_stat_nodes, stat_p -> mem_cur_stat_nodes);
stat_p -> mem_max_stat_bytes = max(stat_p -> mem_max_stat_bytes, stat_p -> mem_cur_stat_bytes);
/* Update the total statistics. */
stat_p -> mem_tot_stat_nodes += stat_p -> mem_cur_stat_nodes;
stat_p -> mem_tot_stat_bytes += stat_p -> mem_cur_stat_bytes;
/* Reset the current statistics. */
stat_p -> mem_cur_stat_nodes = 0;
stat_p -> mem_cur_stat_bytes = 0;
}
/* Save the available byte in the last block. */
life -> mem_last_avail = life -> mem_avail;
/* Recalculate the maximum statistics. */
mem_compute_max_stats(life);
/* Update the totals. */
life -> mem_tot_blocks += life -> mem_cur_blocks;
life -> mem_tot_nodes += life -> mem_cur_nodes;
life -> mem_tot_bytes +=life -> mem_cur_bytes;
life -> mem_tot_waste += life -> mem_cur_waste;
life -> mem_tot_big_blocks += life -> mem_cur_big_blocks;
life -> mem_tot_big_bytes += life -> mem_cur_big_bytes;
life -> mem_cur_blocks = 0;
life -> mem_cur_nodes = 0;
life -> mem_cur_bytes = 0;
life -> mem_cur_waste = 0;
life -> mem_cur_big_blocks = 0;
life -> mem_cur_big_bytes = 0;
/* Actually free the memory! */
lst_free_all_macro(life -> mem_block_list, mem_block);
life -> mem_block_list = NULL;
life -> mem_ptr = NULL;
life -> mem_avail = 0;
STATX(ftag);
}
/*
Free the lifetime node itself and remove it from the list of lifetimes.
*/
void
mem_free_life_node(register mem_life * life)
{
FTAG("mem_free_life_node");
STATB(ftag);
PERM_ASSERT(life);
/* Remove the life from the list of lifetimes. */
if (mem_life_list == life)
{
mem_life_list = life -> mem_life_list;
}
else
{
mem_life * p1 = mem_life_list;
mem_life * p2 = p1 -> mem_life_list;
while(p2 && life != p2)
{
PERM_ASSERT(p2);
p1 = p2;
p2 = p1 -> mem_life_list;
}
p1 -> mem_life_list = p2 -> mem_life_list;
}
/*
`mem_init_life' uses `lib_calloc' to allocate the life
because it assumes nothing has been initialzed.
*/
lib_free(life);
STATX(ftag);
}
/*
Deallocate a statistic node.
*/
void
mem_free_stat_node(mem_stat * the_stat)
{
PERM_ASSERT(the_stat);
lib_free(the_stat);
}
/*
Allocate and initialize a life node.
Assume nothing has been initialized,
so don't use Sherlock macros or obj_new_macro.
*/
mem_life *
mem_init_life(char * life_name)
{
FTAG("mem_init_life");
mem_life * life = NULL;
register mem_life * prev = NULL;
register mem_life * lp = NULL;
life = lib_calloc((size_t) 1, sizeof(mem_life));
if (life == NULL) {
end_abort();
}
life -> mem_life_name = life_name;
/* Add the life to the lifetime list in alphabetical order. */
for (prev = NULL, lp = mem_life_list; lp; prev = lp, lp = lp -> mem_life_list) {
int n;
n = strcmp(life_name, lp -> mem_life_name);
if (n == 0) {
err_fatal2("mem_init_life: duplicate life: ", life_name);
}
if (n < 0) {
break;
}
}
/* Link the type descriptor into the alphabetical list. */
if (prev == NULL) {
/* Link the life node before the first node. */
life -> mem_life_list = mem_life_list;
mem_life_list = life;
}
else {
/* Link the life node after the prev node. */
ASSERT(mem_life_list);
life -> mem_life_list = prev -> mem_life_list;
prev -> mem_life_list = life;
}
return life;
}
/*
Initialize a new statistics node and
attach it to the statistics list for the indicated lifetime.
Assume nothing has been initialized.
*/
mem_stat *
mem_init_stats(register mem_life * life, char * node_tag)
{
FTAG("mem_init_stats");
register mem_stat * prev = NULL;
register mem_stat * sp = NULL;
mem_stat * stat_p = NULL;
if (life == NULL) {
end_abort();
}
/* Create the statistic node. */
stat_p = lib_calloc(1, sizeof(mem_stat));
stat_p -> mem_stat_node_tag = node_tag;
/* Update the maximum width of the tag column. */
mem_max_node_tag = max(mem_max_node_tag, strlen(node_tag)+1);
/* Add the life to the lifetime list in alphabetical order. */
for (
prev = NULL, sp = life -> mem_life_stat_list;
sp;
prev = sp, sp = sp -> next
) {
int n;
n = strcmp(node_tag, sp -> mem_stat_node_tag);
if (n == 0) {
err_fatal2("duplicate life statistic: ", node_tag);
}
if (n < 0) {
break;
}
}
/* Link the stat_p into the alphabetical list. */
if (prev == NULL) {
/* Link the stat_p node before the first node. */
stat_p -> next = life -> mem_life_stat_list;
life -> mem_life_stat_list = stat_p;
}
else {
/* Link the stat_p node after the prev node. */
ASSERT(life -> mem_life_stat_list);
stat_p -> next = prev -> next;
prev -> next = stat_p;
}
return stat_p;
}
/*
Return a pointer to size bytes of memory allocated in some permanent block.
*/
void *
mem_new_big_block(size_t size, register mem_life * life, char * dtag)
{
FTAG("mem_new_big_block");
mem_block * mbp = NULL;
void * result = NULL;
STATB(ftag);
ASSERT(dtag && life && life -> mem_avail >= 0 && size > 0);
/* Update block statistics. */
MEM_STATS(
life -> mem_cur_big_blocks ++;
life -> mem_cur_big_bytes += size;
);
/*
Allocate a big block. Do not change the status variables.
We can't use obj_new_macro here because dtag changes.
*/
obj_new_var_tag_macro(mbp, size + sizeof(mem_block *), dtag);
/* Add the block to the list. */
mbp -> next = life -> mem_block_list;
life -> mem_block_list = mbp;
/* Point result at the first byte. */
result = &(mbp -> mem_data[0]);
TRACEN("-mem_watch", sl_watch(mbp, size + sizeof(mem_block *), dtag));
TRACEPX(ftag,
es(life -> mem_life_name); eblank(); elong(size); eblank();
eret(); eptr(result); enl());
return result;
}
/*
Return a pointer to size bytes of memory allocated in a new block.
A new block is always allocated even if size < life -> mem_avail.
In fact, size may be zero, which allocates an unused new block.
*/
void *
mem_new_block(register size_t size, register mem_life * life)
{
FTAG("mem_new_block");
mem_block * mbp = NULL;
void * result = NULL;
STATB(ftag);
ASSERT(life -> mem_avail >= 0 && size >= 0);
/* Bug fix: 7/13/94: use MEM_ALLOC_SIZE, *not* MEM_BLOCK_SIZE here! */
if (size >= MEM_ALLOC_SIZE) {
result = mem_new_big_block(size, life, life -> mem_life_name);
}
else {
/* Update block statistics. */
MEM_STATS(
life -> mem_cur_blocks++;
life -> mem_cur_waste += life -> mem_avail;
);
/*
Get a standard sized permanent block.
We can't use obj_new_macro here because the tag can change.
*/
obj_new_var_tag_macro(mbp, MEM_BLOCK_SIZE, life -> mem_life_name);
/* Add the block to the list. */
mbp -> next = life -> mem_block_list;
life -> mem_block_list = mbp;
/* Point result at the first byte. */
result = &(mbp -> mem_data[0]);
/* Reserve space in the block. */
life -> mem_ptr = ((char *) result) + size;
life -> mem_avail = MEM_ALLOC_SIZE - size;
TRACEN("-mem_watch", sl_watch(mbp, MEM_BLOCK_SIZE, "mem block"));
}
TRACEPX(ftag,
es(life -> mem_life_name); eblank(); elong(size); eblank();
eret(); eptr(result); enl());
return result;
}